package validator

import (
    "encoding/binary"
    "net"
    "time"
)

// NTP协议常量
const (
    ntpPort        = 123
    ntpVersion     = 0x1B // 版本4，客户端模式
    ntpTimeout     = 10 * time.Second
    ntpEpochOffset = 2208988800
)

// NTPPacket NTP协议数据包结构
type NTPPacket struct {
    Settings       uint8
    Stratum        uint8
    Poll           uint8
    Precision      uint8
    RootDelay      uint32
    RootDispersion uint32
    ReferenceID    uint32
    RefTimestampSec uint64
    RefTimestampFrac uint64
    OrigTimestampSec uint64
    OrigTimestampFrac uint64
    RxTimestampSec   uint64
    RxTimestampFrac   uint64
    TxTimestampSec   uint64
    TxTimestampFrac   uint64
}

// NTPClient NTP客户端
type NTPClient struct {
    Server  string
    Timeout time.Duration
}

// NewNTPClient 创建NTP客户端
func NewNTPClient(server string) *NTPClient {
    return &NTPClient{
        Server:  server,
        Timeout: ntpTimeout,
    }
}

// GetTime 从NTP服务器获取时间
func (c *NTPClient) GetTime() (time.Time, error) {
    conn, err := net.DialTimeout("udp", net.JoinHostPort(c.Server, "123"), c.Timeout)
    if err != nil {
        return time.Time{}, err
    }
    defer conn.Close()
    
    if err := conn.SetDeadline(time.Now().Add(c.Timeout)); err != nil {
        return time.Time{}, err
    }

    // 构建NTP请求包
    packet := make([]byte, 48)
    packet[0] = ntpVersion

    // 发送请求
    if _, err := conn.Write(packet); err != nil {
        return time.Time{}, err
    }

    // 读取响应
    if _, err := conn.Read(packet); err != nil {
        return time.Time{}, err
    }

    // 解析响应（简化解析，主要关注传输时间戳）
    txTimestampSec := binary.BigEndian.Uint32(packet[40:44])
    txTimestampFrac := binary.BigEndian.Uint32(packet[44:48])

    // 转换为Unix时间戳
    seconds := int64(txTimestampSec) - ntpEpochOffset
    nanoseconds := int64(txTimestampFrac) * 1e9 / (1 << 32)

    ntpTime := time.Unix(seconds, nanoseconds)
    return ntpTime, nil
}

// GetTimeWithRetry 带重试的时间获取
func (c *NTPClient) GetTimeWithRetry(retries int) (time.Time, error) {
    var lastErr error
    for i := 0; i < retries; i++ {
        ntpTime, err := c.GetTime()
        if err == nil {
            return ntpTime, nil
        }
        lastErr = err
        time.Sleep(time.Second * time.Duration(i+1))
    }
    return time.Time{}, lastErr
}